/* Copyright 2009
 *
 * This program and the accompanying materials
 * are made available under the terms of the
 * Eclipse Public License v1.0 which accompanies
 * this distribution, and is available at
 *
 * 		http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
 * OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * Contributors:
 * 	   IBM Corporation - initial API and implementation for JDT/DLTK
 *     Sean W. Quinn - initial adoption for use with PHP from various sources.
 */
package org.eclipse.php.internal.core;

import java.util.ArrayList;
import java.util.zip.ZipFile;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.dltk.compiler.CharOperation;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.internal.core.ModelManager;
import org.eclipse.dltk.internal.core.OpenableElementInfo;
import org.eclipse.dltk.internal.core.ScriptProject;
import org.eclipse.dltk.internal.core.util.HashtableOfArrayToObject;
import org.eclipse.php.core.INamespaceFragmentRoot;
import org.eclipse.php.core.IPHPElement;
import org.eclipse.php.core.PHPModelException;

/**
 * A Namespace fragment root that corresponds to a .Phar or .zip.
 * <p>
 * NOTE: The only visible entries from a .Phar or .zip Namespace fragment root
 * are .class files.
 * <p>
 * NOTE: A Phar Namespace fragment root may or may not have an associated
 * resource.
 *
 * @see org.eclipse.jdt.core.INamespaceFragmentRoot
 * @see org.eclipse.jdt.internal.core.PharNamespaceFragmentRootInfo
 */
@SuppressWarnings("restriction")
public class PharNamespaceFragmentRoot extends NamespaceFragmentRoot {

	private final static ArrayList EMPTY_LIST = new ArrayList();

	/**
	 * The path to the Phar file (a workspace relative path if the Phar is
	 * internal, or an OS path if the Phar is external)
	 */
	protected final IPath PharPath;

	/**
	 * Constructs a Namespace fragment root which is the root of the Java
	 * Namespace directory hierarchy based on a Phar file that is not contained
	 * in a <code>IScriptProject</code> and does not have an associated
	 * <code>IResource</code>.
	 */
	protected PharNamespaceFragmentRoot(IPath externalPharPath, ScriptProject project) {
		super(null, project);
		this.PharPath = externalPharPath;
	}

	/**
	 * Constructs a Namespace fragment root which is the root of the Java
	 * Namespace directory hierarchy based on a Phar file.
	 */
	protected PharNamespaceFragmentRoot(IResource resource, ScriptProject project) {
		super(resource, project);
		this.PharPath = resource.getFullPath();
	}

	/**
	 * Compute the Namespace fragment children of this Namespace fragment root.
	 * These are all of the directory zip entries, and any directories implied
	 * by the path of class files contained in the Phar of this Namespace
	 * fragment root.
	 */
	@Override
	protected boolean computeChildren(OpenableElementInfo info, IResource underlyingResource) throws PHPModelException {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#computeChildren() not yet implemented!");
	}

	/**
	 * Returns a new element info for this element.
	 */
	@Override
	protected Object createElementInfo() {
		return new PharNamespaceFragmentRootInfo();
	}

	/**
	 * A Phar is always K_BINARY.
	 */
	@Override
	protected int determineKind(IResource underlyingResource) {
		return INamespaceFragmentRoot.K_BINARY;
	}

	/**
	 * Returns true if this handle represents the same Phar as the given handle.
	 * Two Phars are equal if they share the same zip file.
	 *
	 * @see Object#equals
	 */
	@Override
	public boolean equals(Object o) {
		if (this == o)
			return true;
		if (o instanceof PharNamespaceFragmentRoot) {
			PharNamespaceFragmentRoot other = (PharNamespaceFragmentRoot) o;
			return this.PharPath.equals(other.PharPath);
		}
		return false;
	}

	@Override
	public String getElementName() {
		return this.PharPath.lastSegment();
	}

	/**
	 * Returns the underlying ZipFile for this Phar Namespace fragment root.
	 *
	 * @exception CoreException if an error occurs accessing the Phar
	 */
	public ZipFile getPhar() throws CoreException {
		return ModelManager.getModelManager().getZipFile(getPath());
	}

	/**
	 * @see INamespaceFragmentRoot
	 */
	@Override
	public int getKind() {
		return INamespaceFragmentRoot.K_BINARY;
	}

	@Override
	int internalKind() throws PHPModelException {
		return INamespaceFragmentRoot.K_BINARY;
	}

	/**
	 * Returns an array of non-java resources contained in the receiver.
	 */
	@Override
	public Object[] getNonPHPResources() throws ModelException {
		// We want to show non java resources of the default Namespace at the root (see PR #1G58NB8)
		Object[] defaultPkgResources = ((PharNamespaceFragment) getNamespaceFragment(CharOperation.NO_STRINGS))
				.storedNonPHPResources();
		int length = defaultPkgResources.length;
		if (length == 0)
			return defaultPkgResources;
		Object[] nonJavaResources = new Object[length];
		for (int i = 0; i < length; i++) {
			PharEntryResource nonJavaResource = (PharEntryResource) defaultPkgResources[i];
			nonJavaResources[i] = nonJavaResource.clone(this);
		}
		return nonJavaResources;
	}

	@Override
	public NamespaceFragment getNamespaceFragment(String[] pkgName) {
		return new PharNamespaceFragment(this, pkgName);
	}

	@Override
	public IPath internalPath() {
		if (isExternal()) {
			return this.PharPath;
		}
		else {
			return super.internalPath();
		}
	}

	@Override
	public IResource resource(NamespaceFragmentRoot root) {
		if (this.resource == null) {
			// external Phar
			return null;
		}
		return super.resource(root);
	}

	/**
	 * @see IPHPElement
	 */
	@Override
	public IResource getUnderlyingResource() throws PHPModelException {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#getUnderlyingResource() not yet implemented!");
	}

	@Override
	public int hashCode() {
		return this.PharPath.hashCode();
	}

	/**
	 * @param rawNamespaceInfo
	 * @param entryName
	 * @param isDirectory
	 * @param sourceLevel
	 * @param compliance
	 */
	private void initRawNamespaceInfo(HashtableOfArrayToObject rawNamespaceInfo, String entryName, boolean isDirectory,
			String sourceLevel, String compliance) {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName()
				+ "#initRawNamespaceInfo() not yet implemented for case: LDC2_W!");
	}

	/**
	 * @see INamespaceFragmentRoot
	 */
	@Override
	public boolean isArchive() {
		return true;
	}

	/**
	 * @see INamespaceFragmentRoot
	 */
	@Override
	public boolean isExternal() {
		return getResource() == null;
	}

	/**
	 * Phars and Phar entries are all read only
	 */
	@Override
	public boolean isReadOnly() {
		return true;
	}

	/**
	 * Returns whether the corresponding resource or associated file exists
	 */
	protected boolean resourceExists(IResource underlyingResource) {
		// FIXME: Implement me!
		throw new RuntimeException(getClass().getName() + "#resourceExists() not yet implemented!");
	}

	@Override
	protected void toStringAncestors(StringBuffer buffer) {
		if (isExternal())
			// don't show project as it is irrelevant for external Phar files.
			// also see https://bugs.eclipse.org/bugs/show_bug.cgi?id=146615
			return;
		super.toStringAncestors(buffer);
	}
}
